﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using General.Core;
using General.Core.Data;
using General.Core.Extensions;
using General.Core.Librs;
using General.Entities;
using General.Mvc.Filters;
using General.Services.Students;
using General.Web;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Swashbuckle.AspNetCore.Swagger;
using Serilog;
using Serilog.Core;
using Microsoft.AspNetCore.Authentication.Cookies;
using Panda.DynamicWebApi;
using General.Framework;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using Microsoft.CodeAnalysis.Options;
using General.Mvc.Models;
using Quartz;
using Quartz.Impl;
using General.Entities.Dapper;
using EasyNetQ;
using System.Reflection;
using General.Mvc.Extensions;
using General.Core.UnitOfWork;
using General.Mvc.Middleware;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using Essensoft.AspNetCore.Payment.Alipay;
using Essensoft.AspNetCore.Payment.WeChatPay;
using AutoMapper;
using Microsoft.Extensions.Logging;
using System.Diagnostics;
using QuartzHostedService;
using General.Mvc.Jobs;

namespace General.Mvc
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;

            //初始化dapper工厂
            DbConnectionFactory.Configuration = configuration;
        }
        public IConfiguration Configuration { get; }

        
        public void ConfigureServices(IServiceCollection services)
        {
            services.Configure<CookiePolicyOptions>(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => false;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });


            services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());


            services.AddSession(options => {
                options.IdleTimeout = TimeSpan.FromMinutes(30);//session过期时间
            });

            //链接字符串   poolSize:最大链接数量
            //services.AddDbContextPool<GeneralDbContext>(options =>
            //{
            //    options.UseSqlServer(Configuration.GetConnectionString("SqlServer"));
            //    //options.UseMySql(Configuration.GetConnectionString("MySql"));
            //}, poolSize: 99);
            services.AddDbContext<GeneralDbContext>();

            //读写分离配置 请在GeneralDbContext.cs配置
            services.Configure<DBConnectionOption>(Configuration.GetSection("ConnectionStrings"));

            //注入Uow依赖
            services.AddScoped<IUnitOfWork, UnitOfWork<GeneralDbContext>>();

            //services.AddScoped<IStudentsService, StudentsService>();

            //PostgreSql
            //services.AddDbContextPool<GeneralDbContext>(options =>
            //{
            //    options.UseNpgsql(Configuration.GetConnectionString("PostgreSql"));

            //}, poolSize: 99);

            //创建权限
            services.AddAuthentication();


            //配置redis链接地址
            var csredis = new CSRedis.CSRedisClient(Configuration["RedisConnStr"]);
            //初始化 RedisHelper
            RedisHelper.Initialization(csredis);
            //注册mvc分布式缓存
            services.AddSingleton<IDistributedCache>(new Microsoft.Extensions.Caching.Redis.CSRedisCache(RedisHelper.Instance));

            //日志
            services.AddSingleton((Serilog.ILogger)new LoggerConfiguration()
                .MinimumLevel.Error()
                .WriteTo.File("Logs/log.txt", rollingInterval: RollingInterval.Hour)
                .CreateLogger());
            services.AddSingleton<ResponseLogTimeFilter>();

            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1",new Microsoft.OpenApi.Models.OpenApiInfo() {
                    Title = "荔枝IM接口文档",
                    Version = "v1",
                    Contact =new Microsoft.OpenApi.Models.OpenApiContact()
                    {
                        Name = "冰河洗剑",
                        Email = "834713287@qq.com",
                        Url = new Uri("http://app.ourres.com:81")
                    }
                });
                c.DocInclusionPredicate((docName, description) => true);
                // 为 Swagger JSON and UI设置xml文档注释路径
                var basePath = Path.GetDirectoryName(typeof(Program).Assembly.Location);//获取应用程序所在目录（绝对，不受工作目录影响，建议采用此方法获取路径）
                var xmlPath = Path.Combine(basePath, "General.Mvc.xml");
                c.IncludeXmlComments(xmlPath);
                //c.IncludeXmlComments(@"bin\Debug\netcoreapp2.2\Xc.StuMgr.WebApiHost.xml");
                //c.IncludeXmlComments(@"bin\Debug\netcoreapp2.2\Xc.StuMgr.Application.xml");


            });

            //配置MQ消息队列
            //services.AddSingleton(RabbitHutch.CreateBus(Configuration["MQ:Dev"]));


            //程序集依赖注入
            services.AddAssembly("General.Services");
            //泛型注入到DI里面
            services.AddScoped(typeof(IRepository<>), typeof(EFRepository<>));


            //获得实例
            EngineContext.Initialize(new GeneralEngine(services.BuildServiceProvider()));

            //添加授权支持，并添加使用Cookie的方式，配置登录页面和没有权限时的跳转页面
            services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
                .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, o =>
                {
                    o.LoginPath = new PathString("/Home/Login");            //登录路径：这是当用户试图访问资源但未经过身份验证时，程序将会将请求重定向到这个相对路径。
                    o.AccessDeniedPath = new PathString("/Home/Error");     //禁止访问路径：当用户试图访问资源时，但未通过该资源的任何授权策略，请求将被重定向到这个相对路径。
                    o.SlidingExpiration = true; //Cookie可以分为永久性的和临时性的。 临时性的是指只在当前浏览器进程里有效，浏览器一旦关闭就失效（被浏览器删除）。 永久性的是指Cookie指定了一个过期时间，在这个时间到达之前，此cookie一直有效（浏览器一直记录着此cookie的存在）。 slidingExpriation的作用是，指示浏览器把cookie作为永久性cookie存储，但是会自动更改过期时间，以使用户不会在登录后并一直活动，但是一段时间后却自动注销。也就是说，你10点登录了，服务器端设置的TimeOut为30分钟，如果slidingExpriation为false,那么10: 30以后，你就必须重新登录。如果为true的话，你10: 16分时打开了一个新页面，服务器就会通知浏览器，把过期时间修改为10: 46。
                });

            //jwt验证配置
            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
            .AddJwtBearer(option => {
                option.RequireHttpsMetadata = false;
                option.SaveToken = true;
                option.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidIssuer = JwtSettings.Issuer,
                    ValidAudience = JwtSettings.Audience,
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(JwtSettings.SecretKey))
                };
                option.Events = new JwtBearerEvents
                {
                    //此处为权限验证失败后触发的事件
                    OnChallenge = context =>
                    {
                        //此处代码为终止.Net Core默认的返回类型和数据结果，这个很重要哦，必须
                        context.HandleResponse();

                        //自定义自己想要返回的数据结果，我这里要返回的是Json对象，通过引用Newtonsoft.Json库进行转换
                        var payload = JsonHelper.Serialize(new { Code = "401", Message = "很抱歉，您无权访问该接口；来自冰河洗剑~~" });
                        //自定义返回的数据类型
                        context.Response.ContentType = "application/json";
                        //自定义返回状态码，默认为401 我这里改成 200
                        context.Response.StatusCode = StatusCodes.Status200OK;
                        //context.Response.StatusCode = StatusCodes.Status401Unauthorized;
                        //输出Json数据结果
                        context.Response.WriteAsync(payload);
                        return Task.FromResult(0);
                    }
                };



                //    /***********************************TokenValidationParameters的参数默认值***********************************/
                //    // RequireSignedTokens = true,
                //    // SaveSigninToken = false,
                //    // ValidateActor = false,
                //    // 将下面两个参数设置为false，可以不验证Issuer和Audience，但是不建议这样做。
                //    // ValidateAudience = true,
                //    // ValidateIssuer = true, 
                //    // ValidateIssuerSigningKey = false,
                //    // 是否要求Token的Claims中必须包含Expires
                //    // RequireExpirationTime = true,
                //    // 允许的服务器时间偏移量
                //    // ClockSkew = TimeSpan.FromSeconds(300),
                //    // 是否验证Token有效期，使用当前时间与Token的Claims中的NotBefore和Expires对比
                //    // ValidateLifetime = true
                //};
                //    option.SecurityTokenValidators.Clear();
                //option.SecurityTokenValidators.Add(new TokenValidtor());
                //option.Events = new JwtBearerEvents
                //{
                //    OnMessageReceived = context =>
                //    {
                //        var token = context.Request.Query["access_token"];
                //        if (!string.IsNullOrEmpty(token)) 
                //        {
                //            context.Token = token;
                //        }

                //        return Task.CompletedTask;
                //    }

                //};
            });


            //Quartz.Net -- 注册ISchedulerFactory的实例。
            services.AddSingleton<ISchedulerFactory, StdSchedulerFactory>();
            
            
            // 引入Payment 依赖注入
            services.AddAlipay();
            services.AddWeChatPay();

            // 在 appsettings.json(开发环境：appsettings.Development.json) 中 配置选项
            services.Configure<AlipayOptions>(Configuration.GetSection("Alipay"));
            services.Configure<WeChatPayOptions>(Configuration.GetSection("WeChatPay"));


            services.AddQuartzHostedService()
                  .AddQuartzJob<MyJob>()
                  .AddQuartzJob<RequestUrlJob>()
                  .AddQuartzJob<ClearSystemLogsJob>();


            services.AddRazorPages();

            services.AddMvc(options => {
                options.Filters.Add(typeof(GlobalExceptionFilter));
            }).SetCompatibilityVersion(CompatibilityVersion.Version_3_0);

            

            // 添加动态WebApi 需放在 AddMvc 之后
            services.AddDynamicWebApi();

        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
        {
            var logger = loggerFactory.CreateLogger("系统信息");
            logger.LogInformation("配置初始化中...");
            Stopwatch stopwatch = Stopwatch.StartNew();
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                //app.UseExceptionHandler("/Main/Index");
            }

            //app.UseCors(t =>
            //{
            //    t.WithMethods("POST", "PUT", "GET","OPTIONS");
            //    t.WithHeaders("X-Requested-With", "Content-Type", "User-Agent");
            //    t.WithOrigins("*");
            //});

            //开启跨域
            app.UseCorsMiddleware();

            app.UseSwagger();
            app.UseSwaggerUI(c =>
            {
                c.SwaggerEndpoint("/swagger/v1/swagger.json", "荔枝IM");
            });

            app.UseSession();

            app.UseStaticFiles();
            app.UseCookiePolicy();

            //配置MQ消息队列
            //app.UseSubscribe("ClientMessageService", Assembly.GetExecutingAssembly());



            //发布到centos
            app.UseForwardedHeaders(new ForwardedHeadersOptions
            {
                ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
            });

            //身份认证中间件
            app.UseAuthentication();
           

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
                endpoints.MapRazorPages();
            });


            //consul
            //this.Configuration.ConsulRegist();


            var timeElapsed = stopwatch.Elapsed;
            logger.LogInformation("配置初始化完成 耗时：" + timeElapsed.ToString());
            logger.LogInformation("2020 GeneralNetCore . Powered by 冰河洗剑");
        }
    }
}
